﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SoulDoorEngine.Framework.Screens;
using SoulDoorEngine.Framework.Components;
using SoulDoorEngine.Framework.Services;

namespace SoulDoorEngine.Framework
{
    public class Engine
    {
        private static GraphicsDevice _GraphicsDevice;
        private static SpriteBatch _SpriteBatch;
        private static ScreenCollection _Screens = new ScreenCollection();
        private static GameTime _GameTime;
        private static bool _IsInitialized = false;
        private static SEContentManager _Content;
        private static SEServiceContainer _Services;
        private static Screen _BackgroundScreen;
        private static Screen _DefaultScreen;

        /// <summary>
        /// 引擎默认GameScreen实例
        /// </summary>
        public static Screen DefaultScreen
        {
            get { return _DefaultScreen; }
            set { _DefaultScreen = value; }
        }

        /// <summary>
        /// 背景屏幕
        /// </summary>
        public static Screen BackgroundScreen
        {
            get { return _BackgroundScreen; }
            set { _BackgroundScreen = value; }
        }

        /// <summary>
        /// 引擎的服务容器
        /// </summary>
        public static SEServiceContainer Services
        {
            get { return _Services; }
            set { _Services = value; }
        }

        /// <summary>
        /// 引擎的内容管理器
        /// </summary>
        public static SEContentManager Content
        {
            get { return _Content; }
            set { _Content = value; }
        }

        /// <summary>
        /// 引擎是否已被初始化
        /// </summary>
        public static bool IsInitialized
        {
            get { return _IsInitialized; }
            set { _IsInitialized = value; }
        }

        /// <summary>
        /// 当前游戏时间
        /// </summary>
        public static GameTime GameTime
        {
            get { return _GameTime; }
            set { _GameTime = value; }
        }

        public static ScreenCollection Screens
        {
            get { return _Screens; }
            set { _Screens = value; }
        }
        
        /// <summary>
        /// 引擎的批渲染精灵
        /// </summary>
        public static SpriteBatch SpriteBatch
        {
            get { return _SpriteBatch; }
            set { _SpriteBatch = value; }
        }
        
        /// <summary>
        /// 引擎所使用的图形设备
        /// </summary>
        public static GraphicsDevice GraphicsDevice
        {
            get { return _GraphicsDevice; }
            set { _GraphicsDevice = value; }
        }

        /// <summary>
        /// 初始化引擎
        /// </summary>
        /// <param name="graphicsDeviceService">图形设备服务</param>
        public static void SetupEngine(IGraphicsDeviceService graphicsDeviceService)
        {
            Engine.GraphicsDevice = graphicsDeviceService.GraphicsDevice;
            Engine.SpriteBatch = new SpriteBatch(Engine.GraphicsDevice);

            Engine.IsInitialized = true;

            Engine.Services = new SEServiceContainer();
            Engine.Services.AddService(typeof(IGraphicsDeviceService), graphicsDeviceService);

            Engine.Content = new SEContentManager(Services);

            BackgroundScreen = new Screen("Engine.BackgroundScreen");
            BackgroundScreen.OverrideUpdateBlocked = true;
            BackgroundScreen.OverrideDrawBlocked = true;
            BackgroundScreen.OverrideInputBlocked = true;

            DefaultScreen = BackgroundScreen;
        }

        /// <summary>
        /// 更新所有屏幕
        /// </summary>
        /// <param name="gameTime">游戏时间</param>
        public static void Update(GameTime gameTime)
        {
            Engine.GameTime = gameTime;

            List<Screen> toUpdate = new List<Screen>();

            foreach (Screen item in Screens)
            {
                toUpdate.Add(item);
            }

            //后面的屏幕可以屏蔽前面的屏幕，因此从后往前遍历
            for (int i = Screens.Count - 1; i >= 0; --i)
            {
                if (Screens[i].BlockUpdate)
                {
                    if (i > 0)
                    {
                        for (int j = i - 1; j >= 0; --j)
                        {
                            //遍历之前的所有屏幕，如果不屏蔽更新阻止，那么不采取更新
                            if (!Screens[j].OverrideUpdateBlocked)
                            {
                                toUpdate.Remove(Screens[j]);
                            }
                        }
                    }
                    break;
                }
            }

            foreach (Screen item in toUpdate)
            {
                if (item.Initialized)
                {
                    item.Update();
                }
            }

            //清空以为更新输入做准备
            toUpdate.Clear();

            foreach (Screen item in Screens)
            {
                toUpdate.Add(item);
            }

            for (int i = Screens.Count - 1; i >= 0; --i)
            {
                if (Screens[i].BlockInput)
                {
                    if (i > 0)
                    {
                        for (int j = i - 1; j >= 0; --j)
                        {
                            if (!Screens[j].OverrideInputBlocked)
                            {
                                toUpdate.Remove(Screens[j]);
                            }
                        }
                    }
                    break;
                }
            }

            foreach (Screen item in Screens)
            {
                //若没有被手动禁用...
                if (!item.InputDisabled)
                {
                    //则判断是否在列表中（即是否被其它屏幕禁用）
                    item.IsInputAllowed = toUpdate.Contains(item);
                }
                else
                {
                    item.IsInputAllowed = false;
                }
            }
        }

        /// <summary>
        /// 绘制所有屏幕
        /// </summary>
        /// <param name="gameTime">游戏时间</param>
        /// <param name="predicate"></param>
        public static void Draw(GameTime gameTime, ComponentPredicate predicate)
        {
            Engine.GameTime = gameTime;
            List<Screen> toDraw = new List<Screen>();

            GraphicsDevice.Clear(Color.CornflowerBlue);

            foreach (Screen item in Screens)
            {
                if (item.Visible)
                {
                    toDraw.Add(item);
                }
            }

            for (int i = Screens.Count - 1; i >= 0; --i)
            {
                if (Screens[i].BlockDraw)
                {
                    if (i > 0)
                    {
                        for (int j = i - 1; j >= 0; --j)
                        {
                            if (!Screens[j].OverrideDrawBlocked)
                            {
                                toDraw.Remove(Screens[j]);
                            }
                        }
                    }
                    break;
                }
            }

            foreach (Screen item in toDraw)
            {
                if (item.Initialized)
                {
                    item.Draw(predicate);
                }
            }
        }

        /// <summary>
        /// 绘制所有屏幕
        /// </summary>
        /// <param name="gameTime">游戏时间</param>
        /// <param name="type">组件绘制类型</param>
        public static void Draw(GameTime gameTime, ComponentType type)
        {
            Draw(gameTime, new ComponentTypePredicate(type));
        }

        /// <summary>
        /// 重置到初始化状态
        /// </summary>
        public static void Reset()
        {
            List<Component> destroy = new List<Component>();

            foreach (Screen item in Engine.Screens)
            {
                foreach (Component componet in item.Components)
                {
                    destroy.Add(componet);
                }
            }

            foreach (Component item in destroy)
            {
                item.Disable();
            }

            List<Screen> screenDestroy = new List<Screen>();

            foreach (Screen item in Engine.Screens)
            {
                if (item != Engine.BackgroundScreen)
                {
                    screenDestroy.Add(item);
                }
            }

            foreach (Screen item in screenDestroy)
            {
                item.Disable();
            }

            Engine.Services.Clear();
            Engine.Content.Unload();
        }
    }
}
